<?php defined('SYSPATH') or die('No direct script access.');
/**
 * 财付通支付 (即时到账)
 *
 * @author  Yellow.Chow <aultoale@gmail.com>
 * @version $Id: tenpay.php 90 2012-03-22 02:31:03Z yellow.chow $
 * @license http://www.gnu.org/licenses/gpl-3.0.html    GPL 3
 */
class Payment_Adapter_Tenpay extends Payment_Adapter
{

    /**
     * 支付方式参数
     *
     * @var array
     */
    protected $_config = array(
        'cmdno'            => 1,    // 业务代码，1：支付接口，2：查询接口
        'date'             => NULL, // 交易日期，格式 date('Ymd')
        'bank_type'        => 0,    // 银行类型：财付通支付填0
        'desc'             => NULL, // 交易的商品名称，32个字符16汉字内， 不包含特殊符号
        'purchaser_id'     => NULL, // 用户(买方)的财付通帐户(QQ或EMAIL)
        'bargainor_id'     => NULL, // 商家的商户号，由10~12位以上数字组成
        'transaction_id'   => NULL, // 交易订单号，28位数字，格式：商户号+日期+事务ID
        'sp_billno'        => NULL, // 商户系统内部订单号，仅在对账时提供, 28个字符内
        'total_fee'        => NULL, // 订单总金额
        'fee_type'         => 1,    // 现金支付币种，1：RMB，2：USD，3：HKD (目前只支持人民币)
        'return_url'       => NULL, // 支付响应 URL
        'attach'           => NULL, // 商家数据包，数据包将按原样返回
        'spbill_create_ip' => NULL, // 客户端 IP，为了防止欺诈, 支付时财付通会校验此 IP
        'key'              => NULL, // 商户密钥
    );

    /**
     * 银行类型
     *
     * @var array
     */
    protected $_bank_types = array(
        '0'    => '财付通支付',
        '1001' => '招商银行',
        '1002' => '中国工商银行',
        '1003' => '中国建设银行',
        '1004' => '上海浦东发展银行',
        '1005' => '中国农业银行',
        '1006' => '中国民生银行',
        '1008' => '深圳发展银行',
        '1009' => '兴业银行',
        '1020' => '广州银联',
        '1022' => '北京银行',
        '1028' => '中国交通银行',
        '1032' => '中国光大银行',
    );

    /**
     * 构造方法
     *
     * @param  array  $config
     */
    public function __construct(array $config = array())
    {
        if ( ! isset($config['date']))
        {
            $config['date'] = date('Ymd');
        }
        parent::__construct($config);
    }

    /**
     * 设置订单编号
     *
     * @param  string  $id
     * @return Payment_Adapter_Tenpay
     */
    public function set_id($id)
    {
        $this->_config['sp_billno'] = $id;

        return $this;
    }

    /**
     * 设置订单主题
     *
     * 32 个字符 16 汉字内, 不包含特殊符号, 超出部分自动截取
     *
     * @param  string  $subject
     * @return Payment_Adapter_Tenpay
     */
    public function set_subject($subject)
    {
        $this->_config['desc'] = $subject;

        return $this;
    }

    /**
     * 设置订单总金额
     *
     * @param  float  $amount
     * @return Payment_Adapter_Tenpay
     */
    public function set_amount($amount)
    {
        $this->_config['total_fee'] = $amount * 100;

        return $this;
    }

    /**
     * 设置商户密钥
     *
     * @param  string  $key
     * @return Payment_Adapter_Tenpay
     */
    public function set_key($key)
    {
        $this->_config['key'] = $key;

        return $this;
    }

    /**
     * 获取支付网关地址
     *
     * @return string
     */
    public function get_gateway()
    {
        return 'https://www.tenpay.com/cgi-bin/v1.0/pay_gate.cgi';
    }

    /**
     * 获取表单提交参数
     *
     * @return array
     */
    public function get_params()
    {
        $params = $this->_config;
        unset($params['key']);

        if (empty($params['spbill_create_ip']))
        {
            unset($params['spbill_create_ip']);
        }

        $params['sign'] = $this->get_sign();

        return $params;
    }

    /**
     * 获取数字签名
     *
     * @return string
     */
    public function get_sign()
    {
        $params = $this->_config;

        unset($params['bank_type'], $params['desc'], $params['purchaser_id']);

        if (empty($params['spbill_create_ip']))
        {
            unset($params['spbill_create_ip']);
        }

        return strtoupper(md5(urldecode(http_build_query($params))));
    }

    /**
     * 获取支付 URL
     *
     * @return string
     */
    public function get_payurl()
    {
        return $this->get_gateway().'?'.http_build_query($this->get_params());
    }

    /**
     * 对参数进行校验
     *
     * @return bool
     */
    public function is_valid()
    {
        try
        {
            $config = $this->_config;

            // 业务代码
            if ($config['cmdno'] != 1 AND $config['cmdno'] != 2)
            {
                throw new Payment_Exception('cmdno：错误的业务代码');
            }

            // 交易日期
            if ( ! preg_match('/^\d{8}$/', $config['date']))
            {
                throw new Payment_Exception("date：无效的交易日期格式");
            }
            $year  = substr($config['date'], 0, 4);
            $month = substr($config['date'], 4, 2);
            $day   = substr($config['date'], 6, 2);
            if ( ! checkdate($month, $day, $year))
            {
                throw new Payment_Exception("date：无效的交易日期");
            }

            // 商品名称/订单主题
            $config['desc'] = Str::slice(trim(str_replace(array("'", '"', '%', '<', '>'), '', $config['desc'])), 32);
            if (empty($config['desc']))
            {
                throw new Payment_Exception("desc：商品名称不能为空");
            }
            $config['desc'] = iconv('utf-8', 'gbk', $config['desc']);

            // 银行类型
            if ( ! array_key_exists($config['bank_type'], $this->_bank_types))
            {
                throw new Payment_Exception('bank_type：不支持的银行类型');
            }

            // 用户(买方)的财付通帐户
            if ( ! empty($config['purchaser_id']) AND
               ( ! preg_match('/^[1-9]\d{4,12}$/', $config['purchaser_id']) OR ! Valid::email($config['purchaser_id'])))
            {
                throw new Payment_Exception('purchaser_id：无效的用户财付通帐户');
            }

            // 商家的商户号
            if ( ! preg_match('/^\d{10,12}$/', $config['bargainor_id']))
            {
                throw new Payment_Exception('bargainor_id：无效的商家商户号');
            }

            // 商户系统内部定单号/订单编号
            if ( ! preg_match('/^[a-z0-9\-]{8,28}$/i', $config['sp_billno']))
            {
                throw new Payment_Exception('sp_billno：无效的订单编号');
            }

            /**
             * 交易订单号
             *
             * transaction_id 为28位长的数值，其中前10位为商户网站编号(SPID)，由财付通统一分配；
             * 之后 8 位为订单产生的日期，如20050415；
             * 最后 10 位商户需要保证一天内不同的事务（用户订购一次商品或购买一次服务），其ID不相同。
             * 此财付通订单号必须保持唯一，不能重复，财付通根据此定单号通知商户发货和数据更新等。
             */
            $prefix = substr($config['bargainor_id'], -10).$config['date'];
            $id = str_replace($prefix, '', $config['sp_billno']);
            if (strlen($id) > 10)
            {
                $id = substr($id, -10);
            }
            $config['transaction_id'] = $prefix.str_pad($id, 10, 0, STR_PAD_LEFT);

            // 订单总金额
            if (floatval($config['total_fee']) <= 0)
            {
                throw new Payment_Exception('total_fee：无效的订单总金额');
            }

            // 现金支付币种
            if ($config['fee_type'] != 1)
            {
                throw new Payment_Exception('fee_type：不支持的现金支付币种类型');
            }

            // 支付响应 URL
            if ( ! empty($config['return_url']) AND ! Valid::url($config['return_url']))
            {
                throw new Payment_Exception('return_url：无效的支付响应 URL');
            }

            // 商家数据包
            if (empty($config['attach']))
            {
                throw new Payment_Exception('attach：商家数据包不能为空');
            }
            $config['attach'] = iconv('utf-8', 'gbk', $config['attach']);

            // 客户端 IP 地址
            if ( ! empty($config['spbill_create_ip']) AND ! Valid::ip($config['spbill_create_ip']))
            {
                throw new Payment_Exception('spbill_create_ip：无效的客户端 IP 地址');
            }

            // 商户密钥
            if ( ! preg_match('/^[a-z0-9]{32}$/i', $config['key']))
            {
                throw new Payment_Exception('key：无效的商户密钥');
            }

            $this->_config = $config;

            return TRUE;
        }
        catch (Exception $e)
        {
            $this->_message = $e->getMessage();
            return FALSE;
        }
    }

    /**
     * 获取支付名称
     *
     * @return string
     */
    public function get_name()
    {
        return '财付通即时到账';
    }

}